﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Windows.Input;
using System.Reflection;
using System.Reflection.Emit;
using Phydeaux.Utilities;

namespace Bixas.Tangibilis.Utility
{
    /// <summary>
    /// A wrapper class to reach members of MS.Internal.Commands.CommandHelpers 
    /// </summary>
    static class CommandHelperWrapper
    {
        private static Phydeaux.Utilities.Proc<ICommandSource> s_ExecuteCommandSourceWrapper;

        static CommandHelperWrapper()
        {
            InitWrappers();
        }

        public static void ExecuteCommandSource(ICommandSource commandSource)
        {
            s_ExecuteCommandSourceWrapper(commandSource);
        }

        private static void InitWrappers()
        {
            #region Init ExecuteCommandSource method wrapper

            //retrieve internal type MS.Internal.Commands.CommandHelpers
            Type commandHelpersType = System.Reflection.Assembly.GetAssembly(typeof(System.Windows.Application))
                                                            .GetType("MS.Internal.Commands.CommandHelpers");
            //retrieve internal procedure ExecuteCommandSource(ICommandSource commandSource)
            MethodInfo executeCommandSourceMethod = commandHelpersType.GetMethod("ExecuteCommandSource", System.Reflection.BindingFlags.NonPublic | BindingFlags.Static);
            //create a new DynamicMethod to create a wrapper to call the required method
            DynamicMethod dm = new DynamicMethod(executeCommandSourceMethod.Name,
                                                 MethodAttributes.Static | MethodAttributes.Public, // public static
                                                 CallingConventions.Standard,
                                                 typeof(void),
                                                 new Type[] { typeof(ICommandSource) },
                                                 commandHelpersType,
                                                 true);
            dm.InitLocals = false;

            //use Phydeaux codeEmit wrapper for clener code
            DynamicEmit de = new DynamicEmit(dm);
            de.LoadArgument(0);
            de.Call(executeCommandSourceMethod);
            //TODO: verify if cast is needed when return type is void
            de.CastTo(executeCommandSourceMethod.ReturnType, typeof(void));
            de.Return();
            //Retrieve a typed delegate targeting the created wrapper method
            s_ExecuteCommandSourceWrapper = (Phydeaux.Utilities.Proc<ICommandSource>)dm.CreateDelegate(typeof(Phydeaux.Utilities.Proc<ICommandSource>));
            
            #endregion
        }

    }
}
